﻿using System.Collections;

using zijian666.Core.Abstractions;

namespace zijian666.Core;

/// <summary>
/// 线程安全的列表实现。
/// </summary>
/// <typeparam name="T">列表中元素的类型。</typeparam>
internal class ConcurrentList<T> : IEnumerable<T>
{
    private class DefaultEqualityComparer : IEqualityComparer<T>, IComparer<T>
    {
        public bool Equals(T x, T y)
        {
            if (ReferenceEquals(x, y))
            {
                return true;
            }
            if (x is IEquatable<T> x1)
            {
                return x1.Equals(y);
            }
            return x?.Equals(y) ?? false;
        }

        public int GetHashCode(T value) => value?.GetHashCode() ?? int.MinValue;

        public int Compare(T x, T y)
        {
            if (ReferenceEquals(x, y))
            {
                return 0;
            }
            if (x is IComparable<T> x1)
            {
                return x1.CompareTo(y);
            }
            if (x is IComparable x2)
            {
                return x2.CompareTo(y);
            }
            var a = (x as IOrdered)?.Order ?? 0;
            var b = (y as IOrdered)?.Order ?? 0;
            return a == b ? 0 : a > b ? 1 : -1;
        }
    }

    private static readonly DefaultEqualityComparer DefaultComparer = new DefaultEqualityComparer();

    public ConcurrentList(IEqualityComparer<T>? comparer) => _comparer = comparer ?? DefaultComparer;

    private readonly IEqualityComparer<T> _comparer;
    private readonly List<T> _cache = new List<T>();
    private T[] _items = [];

    /// <summary>
    /// 移除列表中与指定项相等的元素。
    /// </summary>
    /// <param name="item">要移除的元素。</param>
    /// <returns>如果成功移除元素，则返回 true；否则返回 false。</returns>
    public bool Remove(T item) => RemoveIf(x => _comparer.Equals(x, item)) > 0;

    /// <summary>
    /// 根据指定的谓词移除列表中的元素。
    /// </summary>
    /// <param name="predicate">用于定义要移除的元素的条件。</param>
    /// <returns>移除的元素数量。</returns>
    public int RemoveIf(Predicate<T> predicate)
    {
        if (predicate == null)
        {
            return 0;
        }
        lock (_cache)
        {
            var count = _cache.Count;
            while (true)
            {
                var index = _cache.FindLastIndex(predicate);
                if (index <= 0)
                {
                    if (index == 0)
                    {
                        _cache.RemoveAt(0);
                    }
                    var removed = count - _cache.Count;
                    if (removed > 0)
                    {
                        Reflush();
                    }
                    return removed;
                }
                _cache.RemoveAt(index);
            }
        }
    }

    /// <summary>
    /// 向列表中添加一个元素。
    /// </summary>
    /// <param name="item">要添加的元素。</param>
    /// <returns>如果成功添加元素，则返回 true；否则返回 false。</returns>
    public bool Add(T item)
    {
        if (Array.Exists(_items, x => _comparer.Equals(x, item)))
        {
            return false;
        }
        lock (_cache)
        {
            if (_cache.Any(x => _comparer.Equals(x, item)))
            {
                return false;
            }
            _cache.Add(item);
            Reflush();
        }
        return true;
    }

    /// <summary>
    /// 清空列表中的所有元素。
    /// </summary>
    /// <returns>被清空的元素集合。</returns>
    public IEnumerable<T> Clear()
    {
        lock (_cache)
        {
            if (_cache.Count == 0)
            {
                return [];
            }
            _cache.Clear();
            var items = _items;
            _items = [];
            return items;
        }
    }

    /// <summary>
    /// 替换列表中的元素或添加新元素。
    /// </summary>
    /// <param name="item">要替换或添加的元素。</param>
    /// <returns>如果替换了元素，则返回 true；否则返回 false。</returns>
    public bool ReplaceOrAdd(T item)
    {
        lock (_cache)
        {
            var index = _cache.FindIndex(x => _comparer.Equals(x, item));
            if (index < 0)
            {
                _cache.Add(item);
                Reflush();
                return false;
            }
            else
            {
                _cache[index] = item;
                _items[index] = item;
                return true;
            }
        }
    }

    /// <summary>
    /// 向列表中添加一组元素。
    /// </summary>
    /// <param name="items">要添加的元素集合。</param>
    /// <returns>成功添加的元素数量。</returns>
    public int AddRange(IEnumerable<T> items)
    {
        if (_items.Intersect(items, _comparer).Any())
        {
            return 0;
        }
        lock (_cache)
        {
            var i = 0;
            foreach (var item in items)
            {
                if (_cache.Any(x => _comparer.Equals(x, item)))
                {
                    continue;
                }
                _cache.Add(item);
            }
            var count = _cache.Count - _items.Length;
            Reflush();
            return count;
        }
    }

    /// <summary>
    /// 替换列表中的一组元素或添加新元素。
    /// </summary>
    /// <param name="items">要替换或添加的元素集合。</param>
    /// <returns>成功替换的元素数量。</returns>
    public int ReplaceOrAdd(IEnumerable<T> items)
    {
        var count = 0;
        lock (_cache)
        {
            foreach (var item in items)
            {
                var index = _cache.FindIndex(x => _comparer.Equals(x, item));
                if (index < 0)
                {
                    _cache.Add(item);
                }
                else
                {
                    _cache[index] = item;
                    if (index >= 0 && index < _items.Length)
                    {
                        _items[index] = item;
                        count++;
                    }
                }
            }
            if (_cache.Count != _items.Length)
            {
                Reflush();
            }
        }
        return count;
    }

    /// <summary>
    /// 确定列表中是否包含指定的元素。
    /// </summary>
    /// <param name="item">要定位的元素。</param>
    /// <returns>如果在列表中找到该元素，则返回 true；否则返回 false。</returns>
    public bool Contains(T item) => Array.Exists(_items, x => _comparer.Equals(x, item));

    /// <summary>
    /// 确定列表中是否包含满足指定条件的元素。
    /// </summary>
    /// <param name="match">用于定义要搜索的元素的条件。</param>
    /// <returns>如果在列表中找到满足条件的元素，则返回 true；否则返回 false。</returns>
    public bool Contains(Predicate<T> match) => Array.Exists(_items, match);

    /// <summary>
    /// 刷新内部数组，将缓存中的元素按降序排列。
    /// </summary>
    private void Reflush()
        => _items = _cache.OrderByDescending(x => x, DefaultComparer).ToArray();

    /// <summary>
    /// 返回一个循环访问集合的枚举器。
    /// </summary>
    /// <returns>用于循环访问集合的枚举器。</returns>
    public IEnumerator<T> GetEnumerator()
    {
        var items = _items;
        for (int i = 0; i < items.Length; i++)
        {
            yield return items[i];
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator();

    /// <summary>
    /// 返回一个反向循环访问集合的枚举器。
    /// </summary>
    /// <returns>用于反向循环访问集合的枚举器。</returns>
    public IEnumerator<T> Reverse()
    {
        var items = _items;
        for (int i = items.Length - 1; i >= 0; i--)
        {
            yield return items[i];
        }
    }

    /// <summary>
    /// 获取列表中的元素数量。
    /// </summary>
    public int Count => _items.Length;

    /// <summary>
    /// 获取指定索引处的元素。
    /// </summary>
    /// <param name="index">要获取的元素的索引。</param>
    /// <returns>指定索引处的元素，如果索引超出范围，则返回默认值。</returns>
    public T? this[int index] => index < 0 || index >= _items.Length ? default : _items[index];
}
